package cn.enjoy.design.structure.proxy.config;

import cn.enjoy.design.structure.proxy.entity.FlowForm;
import cn.enjoy.design.structure.proxy.event.base.BaseSubmitEvent;
import cn.enjoy.design.structure.proxy.manager.FlowStateManager;
import cn.enjoy.design.structure.proxy.service.FlowService;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.lang.reflect.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @author K
 */
public class FlowProxyFactory {

    private static final Set<String> NOT_TRANSACTION = new HashSet<>(Collections.singletonList("electionAcceptorUser"));
    private FlowStateManager flowStateManager;
    private DataSourceTransactionManager transactionManager;

    public FlowProxyFactory(FlowStateManager flowStateManager, DataSourceTransactionManager transactionManager) {
        this.flowStateManager = flowStateManager;
        this.transactionManager = transactionManager;
    }


    /**
     * 创建代理
     *
     * @param flowService 被代理的服务
     * @return FlowService
     */
    public FlowService proxy(FlowService flowService) {
        FlowStateProxy flowStateProxy = new FlowStateProxy(flowService, flowStateManager, transactionManager);
        return (FlowService) Proxy.newProxyInstance(flowService.getClass().getClassLoader(), new Class[]{FlowService.class}, flowStateProxy);
    }

    class FlowStateProxy implements InvocationHandler {

        private FlowService flowService;
        private FlowStateManager flowStateManager;
        private DataSourceTransactionManager transactionManager;
        private TransactionDefinition transactionDefinition;

        public FlowStateProxy(FlowService flowService, FlowStateManager flowStateManager, DataSourceTransactionManager transactionManager) {
            this.flowService = flowService;
            this.flowStateManager = flowStateManager;
            this.transactionManager = transactionManager;
            this.transactionDefinition = new DefaultTransactionDefinition();
        }

        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            Object result;
            if (!NOT_TRANSACTION.contains(method.getName())) {
                TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
                try {
                    result = exec(method, objects);
                    transactionManager.commit(transactionStatus);
                } catch (Throwable e) {
                    transactionManager.rollback(transactionStatus);
                    throw e;
                }
            } else {
                result = exec(method, objects);
            }
            return result;
        }

        private Object exec(Method method, Object[] objects) throws Throwable {
            try {
                Object result;
                switch (method.getName()) {
                    case "submit":
                        result = submit(method, objects);
                        break;
                    case "trans":
                        result = trans(method, objects);
                        break;
                    case "acceptor":
                        result = acceptor(method, objects);
                        break;
                    case "handle":
                        result = handle(method, objects);
                        break;
                    case "close":
                        result = close(method, objects);
                        break;
                    default:
                        result = method.invoke(flowService, objects);
                }
                return result;
            } catch (Throwable e) {
                if (e instanceof UndeclaredThrowableException || e instanceof InvocationTargetException) {
                    throw e.getCause();
                }
                throw e;
            }
        }

        private Object close(Method method, Object[] objects)throws Throwable {
            Object flowForm = method.invoke(flowService, objects);
//            flowStateManager.publish(new BaseSubmitEvent());
            return flowForm;
        }

        private Object handle(Method method, Object[] objects)throws Throwable {
            Object flowForm = method.invoke(flowService, objects);
//            flowStateManager.publish(new BaseSubmitEvent());
            return flowForm;
        }

        private Object acceptor(Method method, Object[] objects)throws Throwable {
            Object flowForm = method.invoke(flowService, objects);
//            flowStateManager.publish(new BaseSubmitEvent());
            return flowForm;
        }

        private Object trans(Method method, Object[] objects)throws Throwable {
            Object flowForm = method.invoke(flowService, objects);
//            flowStateManager.publish(new BaseSubmitEvent());
            return flowForm;
        }

        private Object submit(Method method, Object[] objects) throws Throwable {
            Object o = method.invoke(flowService, objects);
            flowStateManager.publish(() ->{System.out.println("k");},new BaseSubmitEvent(((FlowForm)o).getId()));
            return o;
        }

    }
}
